use std::{convert::TryFrom, str::FromStr};

use crate::{
    errors::cons_error,
    middlewares::authorize::{club_getters::default_getter, Authorize},
    models::{
        club_form::{ClubFormModelDTO, ClubFormType},
        user::{NormalUserModel, UserRole},
        user_application::UserApplicationModelDTO,
        ClubFormStatusFlowType,
    },
    services::{
        form_service::{
            ApplyApplicationToFormReq, CreateFormToClubReq, FormService, UpdateClubFormReq,
        },
        user_service::UserService,
    },
    utils::{
        authorize::{check_user_has_permission, check_user_has_permission_with_object_id},
        db_utils::{build_filter, COMMON_NO_REGEXP},
        path_params::{PathParamMDB, PathParams},
    },
    AppError, AppResult, PaginationQuery,
};
use actix_http::{Method, StatusCode};
use actix_session::Session;
use actix_web::{web, HttpResponse};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use wither::bson::{doc, oid::ObjectId};

async fn find_all_forms(
    form_service: web::Data<FormService>,
    page_query: web::Query<PaginationQuery>,
) -> AppResult<HttpResponse> {
    let res = form_service.find_all_forms(&page_query, None).await?;

    Ok(HttpResponse::Ok().json(res.map_into::<ClubFormModelDTO>()))
}

async fn search_all_forms(
    form_service: web::Data<FormService>,
    page_query: web::Query<PaginationQuery>,
    where_value: web::Json<Option<Value>>,
) -> AppResult<HttpResponse> {
    let res = form_service
        .find_all_forms(
            &page_query,
            build_filter(
                where_value.as_ref().unwrap_or(&Value::Null),
                &COMMON_NO_REGEXP,
            ),
        )
        .await?;

    Ok(HttpResponse::Ok().json(res.map_into::<ClubFormModelDTO>()))
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateFormCtrlReq {
    pub club_id: String,
    #[serde(rename = "type")]
    pub form_type: ClubFormType,
    pub title: String,
    pub description: Option<String>,
    #[serde(default = "Default::default")]
    pub allow_repeat: bool,
    #[serde(default = "Default::default")]
    pub allow_rewrite: bool,
    pub rule: Option<Value>,
    pub layout: Value,
}

impl TryFrom<CreateFormCtrlReq> for CreateFormToClubReq {
    type Error = AppError;

    fn try_from(value: CreateFormCtrlReq) -> Result<Self, Self::Error> {
        Ok(CreateFormToClubReq {
            club_id: ObjectId::from_str(&value.club_id)?,
            form_type: value.form_type,
            title: value.title,
            description: value.description,
            allow_repeat: value.allow_repeat,
            allow_rewrite: value.allow_rewrite,
            rule: value.rule,
            layout: value.layout,
        })
    }
}

async fn create_form(
    form_service: web::Data<FormService>,
    req: web::Json<CreateFormCtrlReq>,
    user: NormalUserModel,
) -> AppResult<HttpResponse> {
    let req = CreateFormToClubReq::try_from(req.into_inner())?;
    if !check_user_has_permission_with_object_id(user, req.club_id, UserRole::LeaderOrAdmin) {
        return Err(AppError::Status(
            StatusCode::UNAUTHORIZED,
            cons_error::MSG_REQUIRE_AUTHORIZE.into(),
        ));
    }
    let res = form_service.create_form_to_club(req).await?;
    Ok(HttpResponse::Ok().json(ClubFormModelDTO::from(res)))
}
async fn delete_form(
    form_service: web::Data<FormService>,
    path_params: web::Path<PathParams>,
) -> AppResult<HttpResponse> {
    let path_params = PathParamMDB::from(path_params.into_inner());
    let form_id = path_params.form_id.ok_or_else(|| {
        AppError::Status(
            StatusCode::BAD_REQUEST,
            cons_error::MSG_REQUIRE_FORM_ID_IN_PATH.into(),
        )
    })?;
    form_service.delete_form(form_id).await?;
    Ok(HttpResponse::NoContent().finish())
}

async fn update_form(
    form_service: web::Data<FormService>,
    path_params: web::Path<PathParams>,
    req: web::Json<UpdateClubFormReq>,
) -> AppResult<HttpResponse> {
    let path_params = PathParamMDB::from(path_params.into_inner());
    let form_id = path_params.form_id.ok_or_else(|| {
        AppError::Status(
            StatusCode::BAD_REQUEST,
            cons_error::MSG_REQUIRE_FORM_ID_IN_PATH.into(),
        )
    })?;
    let res = form_service
        .update_club_form(form_id, req.into_inner())
        .await?;
    Ok(HttpResponse::Ok().json(ClubFormModelDTO::from(res)))
}

/// ## apply_application_to_form
/// 提交一个指定的表单
async fn apply_application_to_form(
    session: Session,
    form_service: web::Data<FormService>,
    path_params: web::Path<PathParams>,
    req: web::Json<ApplyApplicationToFormReq>,
) -> AppResult<HttpResponse> {
    let user = UserService::get_user_from_session(&session)?;
    let path_params = PathParamMDB::from(path_params.into_inner());
    let form_id = path_params.form_id.ok_or_else(|| {
        AppError::Status(
            StatusCode::BAD_REQUEST,
            cons_error::MSG_FORM_ID_IS_NOT_EXIST.into(),
        )
    })?;
    let form_detail = form_service.form_detail(form_id).await?;
    if !form_detail.valid || !form_detail.active {
        return Err(AppError::Status(
            StatusCode::NOT_FOUND,
            cons_error::MSG_MDB_NOT_FOUND.into(),
        ));
    }
    let res = form_service
        .apply_application_to_form(
            user.id.ok_or_else(|| {
                AppError::Status(
                    StatusCode::BAD_REQUEST,
                    cons_error::MSG_REQUIRE_USER_ID_IN_PATH.into(),
                )
            })?,
            form_detail,
            req.into_inner(),
        )
        .await?;
    Ok(HttpResponse::Ok().json(UserApplicationModelDTO::from(res)))
}

async fn find_all_applications_in_form(
    session: Session,
    form_service: web::Data<FormService>,
    path_params: web::Path<PathParams>,
    page_query: web::Query<PaginationQuery>,
) -> AppResult<HttpResponse> {
    let path_params = PathParamMDB::from(path_params.into_inner());
    let form_id = path_params.form_id.ok_or_else(|| {
        AppError::Status(
            StatusCode::BAD_REQUEST,
            cons_error::MSG_REQUIRE_FORM_ID_IN_PATH.into(),
        )
    })?;
    let user = UserService::get_user_from_session(&session)?;

    let form_detail = form_service.form_detail(form_id).await?;
    // 只有社团社长和管理员可以检查申请信息
    if !check_user_has_permission(
        &user,
        form_detail.club_id.to_string(),
        UserRole::Leader | UserRole::Admin,
    ) {
        return Err(AppError::Status(
            StatusCode::FORBIDDEN,
            cons_error::MSG_REQUIRE_AUTHORIZE.into(),
        ));
    }
    // 草稿状态的提交不被此接口查询
    let res = form_service
        .find_all_application(
            page_query.into_inner(),
            doc! {"form_id":form_id,"status.type":{"$not":{"$eq":ClubFormStatusFlowType::Draft}}},
        )
        .await?;
    Ok(HttpResponse::Ok().json(res.map_into::<UserApplicationModelDTO>()))
}

pub fn configure_form_controller(config: &mut web::ServiceConfig) {
    config
        .service(
            web::resource("")
                .route(web::get().to(find_all_forms))
                .route(web::post().to(create_form))
                .wrap(Authorize::new(UserRole::Admin, default_getter).method(Method::GET)),
        )
        .service(web::resource("/search").route(web::post().to(search_all_forms)))
        .service(
            web::resource("/single/{form_id}")
                .route(web::delete().to(delete_form))
                .route(web::put().to(update_form))
                .route(web::post().to(apply_application_to_form))
                .route(web::get().to(find_all_applications_in_form))
                .wrap(
                    Authorize::new(UserRole::Admin, default_getter)
                        .method(Method::DELETE)
                        .method(Method::PUT),
                )
                .wrap(Authorize::new(UserRole::Guest, default_getter)),
        );
}
